package org.emf.plugins.tomcatlauncher;

import java.io.IOException;

import org.apache.catalina.startup.Bootstrap;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.emf.plugins.tomcatlauncher.server.CommandListener;
import org.emf.plugins.tomcatlauncher.server.KyahServer;
import org.emf.plugins.tomcatlauncher.server.KyahRequest.Command;

/**
 * Start tomcat.
 * @goal start
 */
public final class StartTomcatMojo extends AbstractMojo {

    /**
     * Property name to turn on/off the fork execution. Basically, it determines if process will be waiting
     * for some external stopping signal.<br><br>
     * Uses: -Dtomcatlaucher.fork=true
     */
    private static final String FORK_PROP_NAME = "tomcatlauncher.fork";

    /**
     * Point at your Catalina "build" directory.
     * @parameter expression="${start.catalinaHome}" default-value=""
     */
    protected String catalinaHome;

    /**
     * Base directory for resolving dynamic portions of a Catalina installation.  If not present, resolves to
     * the same directory that {@link #catalinaHome} points to.
     * @parameter expression="${start.catalinaBase}" default-value=""
     */
    protected String catalinaBase;

    /**
     * Server URL including the port part. It not present resolves to http://localhost:8080
     * @parameter expression="${start.serverUrl}" default-value="http://localhost:8080"
     */
    protected String serverUrl;

    /**
     * Indicates if this should create a copy of itself so that it can run in background mode. If not present it
     * defaults to true;
     * @parameter expression="${start.fork}" default-value="true"
     */
    protected boolean fork;

    /**
     * Catalina classloader constructor.
     */
    private Bootstrap bootstrap;

    /**
     * Indicates what is the tomcat server status.
     * @see Status
     */
    private Status status;

    /** Class constructor. */
    public StartTomcatMojo() {
        bootstrap = new Bootstrap();
        status = Status.IDLE;
    }

    /** Gets the tomcat server {@link Status}. */
    public Status getStatus() {
        return status;
    }

    /** Mojo execution. */
    public void execute() throws MojoExecutionException {
        String sFork = System.getProperty(FORK_PROP_NAME);
        if (sFork != null) {
            this.fork = Boolean.valueOf(sFork);
        }
        if (isServerUp()) {
            return;
        }
        try {
            if (catalinaBase == null) {
                catalinaBase = catalinaHome;
            }
            getLog().info(">>> Starting tomcat server...");
//            ExecutorService service = Executors.newCachedThreadPool();
//            requestForStart = System.currentTimeMillis();
//            Future<?> future = service.submit(new StartupThread());
            
            logDebug(">>>START-STARTUP-----------------------------------------------");
            logDebug(">>> catalina.home: " + catalinaHome);
            bootstrap.setCatalinaHome(catalinaHome);
            logDebug(">>> catalina.base: " + catalinaBase);
            bootstrap.setCatalinaBase(catalinaBase);
            logDebug(">>> Init container");
            bootstrap.init();
            logDebug(">>> Start container");
            status = Status.STARTING_UP;
            bootstrap.start();
            getLog().info(">>> Tomcat server is up");
            status = Status.STARTED;
            if (!fork && isServerUp()) {
                getLog().info(">>> Waiting for stop command...");
                final KyahServer kyahServer = new KyahServer();
                CommandListener commandListener = new CommandListener() {
                    @Override
                    public boolean onCommand(Command command) throws Exception {
                        switch (command) {
                        case STOP:
                            status = Status.STOPPING;
                            bootstrap.stop();
                            status = Status.STOPPED;
                            return true;
                        case START:
                            getLog().warn("TODO:START");
                            return false;
                        default:
                            getLog().warn("Invalid command.");
                            return false;
                        }
                    }
                };
                kyahServer.start(commandListener);
            }
        } catch (InterruptedException e) {
            logError("Process interrupted.", e);
        } catch (Exception e) {
            logError("General error.", e);
            new MojoExecutionException("Could not start server.", e);
        }
    }

    /** Logs debug messages. */
    private void logDebug(String msg) {
        if (getLog().isDebugEnabled()) {
            getLog().debug(msg);
        }
    }

    /** Logs error messages. */
    private void logError(String msg, Throwable th) {
        if (th == null) {
            getLog().error("Could not check server availability.");
        } else {
            getLog().error(msg, th);
        }
    }

    /**
     * Checks if server URL is available.
     *
     * @return
     * @since 02/04/2012
     */
    private boolean isServerUp() {
        HttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(serverUrl);
        try {
            HttpResponse response = client.execute(get);
            return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
        } catch (ClientProtocolException e) {
            getLog().warn("There is no server instance available. " + (e.getMessage() != null ? e.getMessage() : ""));
        } catch (IOException e) {
            getLog().warn("There is no server instance available. " + (e.getMessage() != null ? e.getMessage() : ""));
        }
        return false;
    }

    /**
     * Tomcat server status.
     */
    public enum Status {
        /** Sever instance was created but not yet started. */
        IDLE,
        /** Server instance is starting up. */
        STARTING_UP,
        /** Server instance is up and available. */
        STARTED,
        /** Server instance is going down. */
        STOPPING,
        /** Server instance is stopped and invalid. */
        STOPPED;
    }
}
